package moltencore.game.bubble;

import moltencore.game.collision.ICollidable;
import org.apache.log4j.Logger;
import spark.math.Vector2;

import java.awt.*;

public abstract class AbstractBubble implements ICollidable {
	private static Logger logger = Logger.getLogger(AbstractBubble.class);

	//physics information
	public static final float RADIUS = 0.5f;
	public static final float COLLISION_RADIUS = RADIUS * 0.85f;
	public static final float COLLISION_TOLEANCE = 0.000000001f;

	protected short bubbleId;
	protected Type type;
	protected Vector2 position = new Vector2();
	protected Vector2 velocity = new Vector2();

	public AbstractBubble(Type type, short bubbleId) {
		this.type = type;
		this.bubbleId = bubbleId;
	}

	public AbstractBubble(Bubble other) {
		this.bubbleId = other.bubbleId;
		this.type = other.type;
		this.position = new Vector2(other.position);
		this.velocity = new Vector2(other.velocity);
	}

	protected boolean movingTowardsEachOther(AbstractBubble first, AbstractBubble second) {
		Vector2 velocityDifference = Vector2.substract(first.velocity, second.velocity);
		Vector2 positionDifference = Vector2.substract(second.position, first.position);

		return (Vector2.dot(velocityDifference, positionDifference) > 0);
	}

	public Vector2 getVelocity() {
		return velocity;
	}

	public Vector2 getPosition() {
		return position;
	}

	public Type getType() {
		return type;
	}

	public short getBubbleId() {
		return bubbleId;
	}

	public void setBubbleId(short bubbleId) {
		this.bubbleId = bubbleId;
	}

	@Override
	public Vector2 collisionPoint(AbstractBubble bubble, Vector2 movement) {
		if(!collides(bubble, movement)) {
			return null;
		}
		logger.debug("Collidable position is " + position);
		logger.debug("Bubble position is " + bubble.position);
		Vector2 centerDelta = Vector2.substract(bubble.position, position);
		logger.debug("centerDelta is " + centerDelta);
		centerDelta.normalize();
		logger.debug("centerDelta norm is " + centerDelta);
		centerDelta.multiply(COLLISION_RADIUS * 2);
		logger.debug("centerDelta * 2 raduis is " + centerDelta);
		return Vector2.add(position, centerDelta);
	}

	@Override
	public void collisionResponse(AbstractBubble bubble) {
		// if this collidable is sticky the bubble will stick to it and not move
		if(isSticky()) {
			bubble.getVelocity().x = 0;
			bubble.getVelocity().y = 0;
		}
		// else the bubble is reflected
		else {
			throw new RuntimeException("Not implemented");
		}
	}

	@Override
	public boolean collides(AbstractBubble bubble, Vector2 movement) {
		if(movement != null && !movingTowardsEachOther(this, bubble)) {
			return false;
		}
		Vector2 collisionPartnerPosition = (movement == null) ? bubble.position : Vector2.add(bubble.position, movement);
		boolean doCollide =
				Vector2.substract(this.position, collisionPartnerPosition).lengthWithoutSquareRoot() <=
				(Math.pow(COLLISION_RADIUS * 2, 2.0) + COLLISION_TOLEANCE);
		return doCollide;
	}

	@Override
	public boolean isSticky() {
		return true;
	}

	@Override
	public boolean isPartOfWorld() {
		return false;
	}

	@Override
	public boolean equals(Object o) {
		if(this == o) return true;
		if(o == null || getClass() != o.getClass()) return false;

		Bubble bubble = (Bubble) o;

		if(bubbleId != bubble.bubbleId) return false;

		return true;
	}

	@Override
	public int hashCode() {
		return (int) bubbleId;
	}
}
